<div class="content-section introduction">
    <div class="feature-intro">
        <h1>Scroller</h1>
        <p>Scroller is a performant approach to handle huge data efficiently.</p>
    </div>
    <app-demoActions github="scroller" stackblitz="scroller-demo"></app-demoActions>
</div>

<div class="content-section implementation scroller-demo">
    <div class="card">
        <h5>Basic</h5>
        <div class="flex align-items-center flex-wrap gap-3">
            <div>
                <h6>Vertical</h6>
                <p-scroller [items]="basicItems" [itemSize]="50">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">{{item}}</div>
                    </ng-template>
                </p-scroller>
            </div>

            <div>
                <h6>Horizontal</h6>
                <p-scroller [items]="basicItems" [itemSize]="50" orientation="horizontal">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="width: 50px;">{{item}}</div>
                    </ng-template>
                </p-scroller>
            </div>

            <div>
                <h6>Both</h6>
                <p-scroller [items]="multiItems" [itemSize]="[50, 100]" orientation="both">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">
                            <div *ngFor="let el of item" style="width: 100px">{{ el }}</div>
                        </div>
                    </ng-template>
                </p-scroller>
            </div>
        </div>
    </div>

    <div class="card">
        <h5>Scroll Delay</h5>
        <div class="flex align-items-center flex-wrap gap-3">
            <div>
                <h6>0ms Delay</h6>
                <p-scroller [items]="basicItems" [itemSize]="50">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">{{item}}</div>
                    </ng-template>
                </p-scroller>
            </div>

            <div>
                <h6>150ms Delay</h6>
                <p-scroller [items]="basicItems" [itemSize]="50" [delay]="150">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">{{item}}</div>
                    </ng-template>
                </p-scroller>
            </div>

            <div>
                <h6>250ms Delay</h6>
                <p-scroller [items]="basicItems" [itemSize]="50" [delay]="250">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">{{item}}</div>
                    </ng-template>
                </p-scroller>
            </div>
        </div>
    </div>

    <div class="card">
        <h5>Loading</h5>
        <div class="flex align-items-center flex-wrap gap-3">
            <div>
                <h6>Basic</h6>
                <p-scroller [items]="basicItems" [itemSize]="50" [showLoader]="true" [delay]="250">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">{{item}}</div>
                    </ng-template>
                </p-scroller>
            </div>

            <div>
                <h6>Templating</h6>
                <p-scroller class="custom-loading" [items]="basicItems" [itemSize]="50" [showLoader]="true" [delay]="250">
                    <ng-template pTemplate="item" let-item let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">{{item}}</div>
                    </ng-template>
                    <ng-template pTemplate="loader" let-options="options">
                        <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">
                            <p-skeleton [width]="options.even ? '60%' : '50%'" height="1.3rem"></p-skeleton>
                        </div>
                    </ng-template>
                </p-scroller>
            </div>
        </div>
    </div>

    <div class="card">
        <h5>Lazy</h5>
        <p-scroller [items]="lazyItems" [itemSize]="50" [showLoader]="true" [delay]="250" [loading]="lazyLoading" [lazy]="true" (onLazyLoad)="onLazyLoad($event)">
            <ng-template pTemplate="item" let-item let-options="options">
                <div class="scroll-item p-2" [ngClass]="{'odd': options.odd}" style="height: 50px;">{{item}}</div>
            </ng-template>
        </p-scroller>
    </div>

    <div class="card">
        <h5>Template</h5>
        <p-scroller class="custom-loading" [items]="templateItems" [itemSize]="25 * 7" [showLoader]="true" [delay]="250">
            <ng-template pTemplate="item" let-item let-options="options">
                <div class="custom-scroll-item scroll-item" [ngClass]="{'odd': options.odd}">
                    <div class="flex align-items-center px-2" style="height: 25px">Item: {{item}}</div>
                    <div class="flex align-items-center px-2" style="height: 25px">Index: {{options.index}}</div>
                    <div class="flex align-items-center px-2" style="height: 25px">Count: {{options.count}}</div>
                    <div class="flex align-items-center px-2" style="height: 25px">First: {{options.first}}</div>
                    <div class="flex align-items-center px-2" style="height: 25px">Last: {{options.last}}</div>
                    <div class="flex align-items-center px-2" style="height: 25px">Even: {{options.even}}</div>
                    <div class="flex align-items-center px-2" style="height: 25px">Odd: {{options.odd}}</div>
                </div>
            </ng-template>
            <ng-template pTemplate="loader" let-options="options">
                <div class="custom-scroll-item scroll-item" [ngClass]="{'odd': options.odd}">
                    <div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
                    <div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="50%" height="1.2rem"></p-skeleton></div>
                    <div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
                    <div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="50%" height="1.2rem"></p-skeleton></div>
                    <div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
                    <div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="50%" height="1.2rem"></p-skeleton></div>
                    <div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
                </div>
            </ng-template>
        </p-scroller>
    </div>
</div>

<div class="content-section documentation">
    <p-tabView>
        <p-tabPanel header="Documentation">
            <h5>Import</h5>
<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
import &#123;ScrollerModule&#125; from 'primeng/scroller';
</app-code>

            <h5>Getting Started</h5>
            <p>Scroller requires a collection of items as its value, height of an item size, height of the scrollable viewport and a ng-template to display
                    where each item can be accessed using the implicit variable and scroll options can be accessed using the options variable. Scroller automatically calculates how many items will be displayed in the view according to <i>itemSize</i> using a specified scroll height.
                    Its scroll height can be adjusted with <i>scrollHeight</i> property or height property of CSS.</p>

            <p>Throughout the samples, a car interface having vin, brand, year and color properties are used
                to define an object to be displayed by the Scroller. Cars are loaded by a CarService that
                connects to a server to fetch the cars with a Promise. Note that this is for demo purposes only,
                any data source such as an Observable can be used as an alternative as well.</p>
<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
export interface Car &#123;
    vin;
    year;
    brand;
    color;
&#125;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
import &#123; HttpClient &#125; from '@angular/common/http';
import &#123; Injectable &#125; from '@angular/core';

import &#123; Car &#125; from '../domain/car';

@Injectable()
export class CarService &#123;

    constructor(private http: HttpClient) &#123;&#125;

    getCarsSmall() &#123;
        return this.http.get('/showcase/resources/data/cars-small.json')
                    .toPromise()
                    .then(res => &lt;Car[]&gt; res.data)
                    .then(data => &#123; return data; &#125;);
    &#125;
&#125;
</app-code>

            <p>Here is a sample Scroller that displays a list of cars loaded from a remote datasource.</p>
<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
export class ScrollerDemo implements OnInit &#123;

    cars: Car[];

    constructor(private carService: CarService) &#123; &#125;

    ngOnInit() &#123;
        this.carService.getCarsLarge().then(cars => this.cars = cars);
    &#125;
&#125;
</app-code>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-scroller [value]="cars" scrollHeight="200px" [itemSize]="50"&gt;
    &lt;ng-template pTemplate="item" let-car let-scrollOptions="options"&gt;
        Car content
    &lt;/ng-template&gt;
&lt;/p-scroller&gt;
</app-code>

            <h5>Loader</h5>
            <p>Scroller has a special loader. It can be activated with the <i>showLoader</i> property.
                In addition, <i>loader</i> or <i>loadericon</i> templates can be used to add custom loaders to item elements.</p>
<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-scroller [value]="cars" scrollHeight="200px" [itemSize]="50" [showLoader]="true" [delay]="250"&gt;
    &lt;ng-template pTemplate="item" let-car let-scrollOptions="options"&gt;
        Car content
    &lt;/ng-template&gt;
    &lt;ng-template pTemplate="loader" let-scrollOptions="options"&gt;
        &lt;p [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;
            Loading...
        &lt;/p&gt;
    &lt;/ng-template&gt;
&lt;/p-scroller&gt;
</app-code>

            <h6>Scroll Options</h6>
<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;ng-template pTemplate="item" let-item let-scrollOptions="options"&gt;
    // item: Current item.
    // scrollOptions.index: Index of the item.
    // scrollOptions.count: Total numbers of items.
    // scrollOptions.first: Whether this is the first item.
    // scrollOptions.last: Whether this is the last item.
    // scrollOptions.even: Whether the index is even.
    // scrollOptions.odd: Whether the index is odd.
&lt;/ng-template&gt;
&lt;ng-template pTemplate="loader" let-scrollOptions="options"&gt;
    // scrollOptions.index: Index of the item.
    // scrollOptions.count: Total numbers of items.
    // scrollOptions.first: Whether this is the first item.
    // scrollOptions.last: Whether this is the last item.
    // scrollOptions.even: Whether the index is even.
    // scrollOptions.odd: Whether the index is odd.
    // scrollOptions.numCols: Total number of columns in a row in 'both' orientation mode in view.
&lt;/ng-template&gt;
&lt;ng-template pTemplate="loadericon" let-scrollOptions="options"&gt;
    // scrollOptions.styleClass: Style class of the default icon.
&lt;/ng-template&gt;
</app-code>

            <h5>Lazy Loading</h5>
            <p>Lazy mode is handy to deal with large datasets where instead of loading the entire data, small chunks of data are loaded on demand by invoking
             onLazyLoad callback everytime scrolling requires a new chunk. To implement lazy loading,
            enable <i>lazy</i> attribute, initialize your data as a placeholder with a length and finally implement a method callback using <i>onLazyLoad</i> that actually loads a chunk from a datasource. onLazyLoad gets an event object
            that contains information about the chunk of data to load such as the index and number of items to load. Notice that a new template called loadingItem is also required to display as a placeholder while the new items are being loaded.</p>
<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-scroller [value]="virtualCars" scrollHeight="200px" [itemSize]="50"
    [lazy]="true" (onLazyLoad)="loadCarsLazy($event)"&gt;
    &lt;ng-template pTemplate="item" let-car&gt;
        Car content
    &lt;/ng-template&gt;
    &lt;ng-template pTemplate="loader"&gt;
        Loading...
    &lt;/ng-template&gt;
&lt;/p-scroller&gt;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
export class LazyScrollerDemo implements OnInit &#123;

    virtualCars: Car[];

    ngOnInit() &#123;
        this.cars = Array.from(&#123;length: 10000&#125;).map(() => this.carService.generateCar());
        this.virtualCars =  Array.from(&#123;length: 10000&#125;);
    &#125;

    loadCarsLazy(event: LazyLoadEvent) &#123;
        //simulate remote connection with a timeout
        setTimeout(() => &#123;
            //load data of required page
            let loadedCars = this.cars.slice(event.first, event.last);

            //populate page of virtual cars
            Array.prototype.splice.apply(this.virtualCars, [...[event.first, event.rows], ...loadedCars]);

            //trigger change detection
            this.virtualCars = [...this.virtualCars];
        &#125;, 1000);
    &#125;

&#125;
</app-code>

            <h5>Properties</h5>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Type</th>
                            <th>Default</th>
                            <th>Description</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>id</td>
                            <td>string</td>
                            <td>null</td>
                            <td>Unique identifier of the element.</td>
                        </tr>
                        <tr>
                            <td>style</td>
                            <td>object</td>
                            <td>null</td>
                            <td>Inline style of the component.</td>
                        </tr>
                        <tr>
                            <td>styleClass</td>
                            <td>any</td>
                            <td>null</td>
                            <td>Style class of the component.</td>
                        </tr>
                        <tr>
                            <td>tabindex</td>
                            <td>number</td>
                            <td>null</td>
                            <td>Index of the element in tabbing order.</td>
                        </tr>
                        <tr>
                            <td>items</td>
                            <td>array</td>
                            <td>null</td>
                            <td>An array of objects to display.</td>
                        </tr>
                        <tr>
                            <td>itemSize</td>
                            <td>number / [number, number]</td>
                            <td>null</td>
                            <td>The height/width of item according to orientation.</td>
                        </tr>
                        <tr>
                            <td>scrollHeight</td>
                            <td>string</td>
                            <td>null</td>
                            <td>Height of the scroll viewport.</td>
                        </tr>
                        <tr>
                            <td>scrollWidth</td>
                            <td>string</td>
                            <td>null</td>
                            <td>Width of the scroll viewport.</td>
                        </tr>
                        <tr>
                            <td>orientation</td>
                            <td>string</td>
                            <td>'vertical'</td>
                            <td>The orientation of scrollbar, valid values are 'vertical', 'horizontal' and 'both'.</td>
                        </tr>
                        <tr>
                            <td>delay</td>
                            <td>number</td>
                            <td>0</td>
                            <td>Delay in scroll before new data is loaded.</td>
                        </tr>
                        <tr>
                            <td>resizeDelay</td>
                            <td>number</td>
                            <td>10</td>
                            <td>Delay after window's resize finishes.</td>
                        </tr>
                        <tr>
                            <td>lazy</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Defines if data is loaded and interacted with in lazy manner.</td>
                        </tr>
                        <tr>
                            <td>disabled</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>If disabled, the scroller feature is eliminated and the content is displayed directly.</td>
                        </tr>
                        <tr>
                            <td>loaderDisabled</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Used to implement a custom loader instead of using the loader feature in the scroller.</td>
                        </tr>
                        <tr>
                            <td>loading</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Whether the data is loaded.</td>
                        </tr>
                        <tr>
                            <td>showSpacer</td>
                            <td>boolean</td>
                            <td>true</td>
                            <td>Used to implement a custom spacer instead of using the spacer feature in the scroller.</td>
                        </tr>
                        <tr>
                            <td>showLoader</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Whether to show loader.</td>
                        </tr>
                        <tr>
                            <td>numToleratedItems</td>
                            <td>number</td>
                            <td>null</td>
                            <td>Determines how many additional elements to add to the DOM outside of the view. <br />
                                According to the scrolls made up and down, extra items are added in a certain algorithm in the form of multiples of this number. <br />
                                Default value is half the number of items shown in the view.</td>
                        </tr>
                        <tr>
                            <td>autoSize</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Whether to dynamically change the height or width of scrollable container.</td>
                        </tr>
                        <tr>
                            <td>trackBy</td>
                            <td>Function</td>
                            <td>null</td>
                            <td>Function to optimize the dom operations by delegating to ngForTrackBy, default algoritm checks for object identity.</td>
                        </tr>
                        <tr>
                            <td>options</td>
                            <td>ScrollerOptions</td>
                            <td>false</td>
                            <td>Whether to use the scroller feature. The properties of scroller component can be used like an object in it.</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Events</h5>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Parameters</th>
                            <th>Description</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>onScroll</td>
                            <td>event: Browser event</td>
                            <td>Callback to invoke when scroll position changes.</td>
                        </tr>
                        <tr>
                            <td>onScrollIndexChange</td>
                            <td>event.first: First index of the new data range to be loaded.<br/>
                                event.last: Last index of the new data range to be loaded.
                            </td>
                            <td>Callback to invoke when scroll position and item's range in view changes.</td>
                        </tr>
                        <tr>
                            <td>onLazyLoad</td>
                            <td>event.first: First index of the new data range to be loaded.<br/>
                                event.last: Last index of the new data range to be loaded.
                            </td>
                            <td>Callback to invoke in lazy mode to load new data.</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Methods</h5>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Parameters</th>
                            <th>Description</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>scrollTo</td>
                            <td>
                                left: Left position of scroll. <br />
                                top: Top position of scroll <br />
                                behavior: Behavior of scroll, valid values are 'auto' and 'smooth'
                            </td>
                            <td>Scroll to move to a specific position.</td>
                        </tr>
                        <tr>
                            <td>scrollToIndex</td>
                            <td>
                                index: Index of item according to orientation mode. <br />
                                behavior: Behavior of scroll, valid values are 'auto' and 'smooth'
                            </td>
                            <td>Scroll to move to a specific item.</td>
                        </tr>
                        <tr>
                            <td>scrollInView</td>
                            <td>
                                index: Index of item according to orientation mode. <br />
                                to: Defines the location of the item in view, valid values are 'to-start' and 'to-end'. <br />
                                behavior: Behavior of scroll, valid values are 'auto' and 'smooth'
                            </td>
                            <td>It is used to move the specified index into the view. It is a method that will usually be needed when keyboard support is added to the scroller component.</td>
                        </tr>
                        <tr>
                            <td>getRenderedRange</td>
                            <td>-</td>
                            <td>Returns the range of items added to the DOM.</td>
                        </tr>
                        <tr>
                            <td>getElementRef</td>
                            <td>-</td>
                            <td>Returns the reference of scroller container.</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Templates</h5>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                        <thead>
                            <tr>
                                <th>Name</th>
                                <th>Parameters</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>header</td>
                                <td>-</td>
                            </tr>
                            <tr>
                                <td>item</td>
                                <td>$implicit: Data of the option <br />
                                    options: Options of the scroller
                                </td>
                            </tr>
                            <tr>
                                <td>loader</td>
                                <td>options: Options of the scroller on loading</td>
                            </tr>
                            <tr>
                                <td>loadericon</td>
                                <td>options: Options of the scroller on loading</td>
                            </tr>
                        </tbody>
                </table>
            </div>

            <h5>Styling</h5>
            <p>Following is the list of structural style classes, for theming classes visit <a href="#" [routerLink]="['/theming']">theming page</a>.</p>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Element</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>p-scroller</td>
                            <td>Container element.</td>
                        </tr>
                        <tr>
                            <td>p-scroller-content</td>
                            <td>Content element.</td>
                        </tr>
                        <tr>
                            <td>p-scroller-loader</td>
                            <td>Loader element.</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Dependencies</h5>
            <p>None.</p>
        </p-tabPanel>

        <p-tabPanel header="Source">
            <a href="https://github.com/primefaces/primeng/tree/master/src/app/showcase/components/scroller" class="btn-viewsource" target="_blank">
                <span>View on GitHub</span>
            </a>
            <!--<a href="https://stackblitz.com/edit/primeng-scroller-demo" class="btn-viewsource" style="margin-left: .5em;" target="_blank">
                <span>Edit in StackBlitz</span>
            </a>-->
<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;div class="scroller-demo"&gt;
    &lt;div class="card"&gt;
        &lt;h5&gt;Basic&lt;/h5&gt;
        &lt;div class="flex align-items-center flex-wrap gap-3"&gt;
            &lt;div&gt;
                &lt;h6&gt;Vertical&lt;/h6&gt;
                &lt;p-scroller [items]="basicItems" [itemSize]="50"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;

            &lt;div&gt;
                &lt;h6&gt;Horizontal&lt;/h6&gt;
                &lt;p-scroller [items]="basicItems" [itemSize]="50" orientation="horizontal"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="width: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;

            &lt;div&gt;
                &lt;h6&gt;Both&lt;/h6&gt;
                &lt;p-scroller [items]="multiItems" [itemSize]="[50, 100]" orientation="both"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;
                            &lt;div *ngFor="let el of item" style="width: 100px"&gt;&#123;&#123; el &#125;&#125;&lt;/div&gt;
                        &lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
        &lt;h5&gt;Scroll Delay&lt;/h5&gt;
        &lt;div class="flex align-items-center flex-wrap gap-3"&gt;
            &lt;div&gt;
                &lt;h6&gt;0ms Delay&lt;/h6&gt;
                &lt;p-scroller [items]="basicItems" [itemSize]="50"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;

            &lt;div&gt;
                &lt;h6&gt;150ms Delay&lt;/h6&gt;
                &lt;p-scroller [items]="basicItems" [itemSize]="50" [delay]="150"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;

            &lt;div&gt;
                &lt;h6&gt;250ms Delay&lt;/h6&gt;
                &lt;p-scroller [items]="basicItems" [itemSize]="50" [delay]="250"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
        &lt;h5&gt;Loading&lt;/h5&gt;
        &lt;div class="flex align-items-center flex-wrap gap-3"&gt;
            &lt;div&gt;
                &lt;h6&gt;Basic&lt;/h6&gt;
                &lt;p-scroller [items]="basicItems" [itemSize]="50" [showLoader]="true" [delay]="250"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;

            &lt;div&gt;
                &lt;h6&gt;Templating&lt;/h6&gt;
                &lt;p-scroller class="custom-loading" [items]="basicItems" [itemSize]="50" [showLoader]="true" [delay]="250"&gt;
                    &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;/ng-template&gt;
                    &lt;ng-template pTemplate="loader" let-options="options"&gt;
                        &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;
                            &lt;p-skeleton [width]="options.even ? '60%' : '50%'" height="1.3rem"&gt;&lt;/p-skeleton&gt;
                        &lt;/div&gt;
                    &lt;/ng-template&gt;
                &lt;/p-scroller&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
        &lt;h5&gt;Lazy&lt;/h5&gt;
        &lt;p-scroller [items]="lazyItems" [itemSize]="50" [showLoader]="true" [delay]="250" [loading]="lazyLoading" [lazy]="true" (onLazyLoad)="onLazyLoad($event)"&gt;
            &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                &lt;div class="scroll-item p-2" [ngClass]="&#123;'odd': options.odd&#125;" style="height: 50px;"&gt;&#123;&#123;item&#125;&#125;&lt;/div&gt;
            &lt;/ng-template&gt;
        &lt;/p-scroller&gt;
    &lt;/div&gt;

    &lt;div class="card"&gt;
        &lt;h5&gt;Template&lt;/h5&gt;
        &lt;p-scroller class="custom-loading" [items]="templateItems" [itemSize]="25 * 7" [showLoader]="true" [delay]="250"&gt;
            &lt;ng-template pTemplate="item" let-item let-options="options"&gt;
                &lt;div class="custom-scroll-item scroll-item" [ngClass]="&#123;'odd': options.odd&#125;"&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;Item: &#123;&#123;item&#125;&#125;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;Index: &#123;&#123;options.index&#125;&#125;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;Count: &#123;&#123;options.count&#125;&#125;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;First: &#123;&#123;options.first&#125;&#125;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;Last: &#123;&#123;options.last&#125;&#125;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;Even: &#123;&#123;options.even&#125;&#125;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;Odd: &#123;&#123;options.odd&#125;&#125;&lt;/div&gt;
                &lt;/div&gt;
            &lt;/ng-template&gt;
            &lt;ng-template pTemplate="loader" let-options="options"&gt;
                &lt;div class="custom-scroll-item scroll-item" [ngClass]="&#123;'odd': options.odd&#125;"&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;&lt;p-skeleton width="60%" height="1.2rem"&gt;&lt;/p-skeleton&gt;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;&lt;p-skeleton width="50%" height="1.2rem"&gt;&lt;/p-skeleton&gt;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;&lt;p-skeleton width="60%" height="1.2rem"&gt;&lt;/p-skeleton&gt;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;&lt;p-skeleton width="50%" height="1.2rem"&gt;&lt;/p-skeleton&gt;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;&lt;p-skeleton width="60%" height="1.2rem"&gt;&lt;/p-skeleton&gt;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;&lt;p-skeleton width="50%" height="1.2rem"&gt;&lt;/p-skeleton&gt;&lt;/div&gt;
                    &lt;div class="flex align-items-center px-2" style="height: 25px"&gt;&lt;p-skeleton width="60%" height="1.2rem"&gt;&lt;/p-skeleton&gt;&lt;/div&gt;
                &lt;/div&gt;
            &lt;/ng-template&gt;
        &lt;/p-scroller&gt;
    &lt;/div&gt;
&lt;/div&gt;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
import &#123; Component, OnInit, ViewEncapsulation &#125; from '@angular/core';

@Component(&#123;
    templateUrl: './scrollerdemo.html',
    styleUrls: ['./scrollerdemo.scss'],
    encapsulation: ViewEncapsulation.None
&#125;)
export class ScrollerDemo implements OnInit &#123;

    basicItems: string[];

    multiItems: string[][];

    templateItems: string[];

    lazyItems: string[];

    lazyLoading: boolean = true;

    loadLazyTimeout: any;

    ngOnInit() &#123;
        this.basicItems = Array.from(&#123; length: 100000 &#125;).map((_, i) =&gt; `Item #$&#123;i&#125;`);
        this.multiItems = Array.from(&#123; length: 1000 &#125;).map((_, i) =&gt; Array.from(&#123; length: 1000 &#125;).map((_j, j) =&gt; `Item #$&#123;i&#125;_$&#123;j&#125;`));
        this.lazyItems = Array.from(&#123; length: 100000 &#125;);
        this.templateItems = Array.from(&#123; length: 10000 &#125;).map((_, i) =&gt; `Item #$&#123;i&#125;`);
    &#125;

    onLazyLoad(event) &#123;
        this.lazyLoading = true;

        if (this.loadLazyTimeout) &#123;
            clearTimeout(this.loadLazyTimeout);
        &#125;

        //imitate delay of a backend call
        this.loadLazyTimeout = setTimeout(() =&gt; &#123;
            const &#123; first, last &#125; = event;
            const lazyItems = [...this.lazyItems];

            for (let i = first; i &lt; last; i++) &#123;
                lazyItems[i] = `Item #$&#123;i&#125;`;
            &#125;

            this.lazyItems = lazyItems;
            this.lazyLoading = false;
        &#125;, Math.random() * 1000 + 250);
    &#125;
&#125;
</app-code>
<app-code lang="css" ngNonBindable ngPreserveWhitespaces>
.scroller-demo &#123;
    .scroll-item &#123;
        display: flex;
        align-items: center;
    &#125;

    .custom-scroll-item &#123;
        flex-direction: column;
        align-items: stretch;
    &#125;

    .odd &#123;
        background-color: var(--surface-b);
    &#125;

    .p-scroller &#123;
        height: 200px;
        width: 200px;
        border: 1px solid var(--surface-d);
    &#125;

    .p-horizontal-scroll &#123;
        .p-scroller-content &#123;
            display: flex;
            flex-direction: row;
        &#125;

        .scroll-item &#123;
            writing-mode: vertical-lr;
        &#125;
    &#125;

    p-skeleton &#123;
        width: 100%;
    &#125;
&#125;
</app-code>
        </p-tabPanel>
        <!--<p-tabPanel header="StackBlitz">
            <ng-template pTemplate="content">
                <iframe src="https://stackblitz.com/edit/primeng-scroller-demo?embed=1" style="width: 100%; height: 768px; border: none;"></iframe>
            </ng-template>
        </p-tabPanel>-->
    </p-tabView>
</div>
